#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/dir.h>
#include <sys/stat.h>

#include "bstrlib.h"
#include "server.h"
#include "common.h"
#include "receive.h"
#include "send_file_listing.h"
#include "send_metadata.h"
#include "send_slice.h"
#include "fd_list.h"

void receive_info(fd_list list, int i){
    int aux_recv=0, size=0, offset=0, amount=0, fd_client=0, j=0;
    bstring command, command_aux;
    struct bstrList *command_list;
    char *aux_buf = NULL, *buf_server = NULL, *aux = NULL, *end_ptr = NULL;
    /*cargo los nombres de los comandos en estructuras bstring para poder
    luego hacer las comparaciones*/

    bstring gfl = bfromcstr ("get_file_listing");
    bstring gm = bfromcstr ("get_metadata");
    bstring gs = bfromcstr ("get_slice");
    bstring q = bfromcstr ("quit");

    fd_client = fd_list_get_fd(list, i);
    assert(fd_client != 0);
    /*Creamos el buffer de lectura*/

    /*en buf_server vamos a tener el pedido del cliente, letter la usamos
    para guardar caracteres y aux_buf lo usamos para ir guardando lo que
    el cliente nos envia*/
    aux_buf = calloc(BUF_LENGTH, sizeof(char*));
    if(aux_buf == NULL){
        perror("malloc-aux_buf");
        bdestroy(gfl);
        bdestroy(gm);
        bdestroy(gs);
        bdestroy(q);
        send_msg(fd_client, MSG199);
        close(fd_client);
        fd_list_remove(list, i);
        return;
    }
    /*En este bucle vamos a ir recibiendo del cliente hasta que nos
    mande un \r\n*/
    aux_recv = recv(fd_client, aux_buf, BUF_LENGTH, 0);
    if (aux_recv == -1){
        perror("recv");
    }
    if (aux_recv == 0){
        free(aux_buf);
        aux_buf=NULL;
        bdestroy(gfl);
        bdestroy(gm);
        bdestroy(gs);
        bdestroy(q);
        close(fd_client);
        fd_list_remove(list, i);
        return;
    }

    for(j=0;j<aux_recv;j++){
        if (aux_buf[j]=='\r'){
            if (aux_buf[j+1]=='\n'){
                fd_list_set_recv(list, i, true);
                j = j+2;
                break;
            }
            else{
                send_msg(fd_client, MSG100);
                free(aux_buf);
                aux_buf=NULL;
                bdestroy(gfl);
                bdestroy(gm);
                bdestroy(gs);
                bdestroy(q);
                close(fd_client);
                fd_list_remove(list, i);
                return;
            }
        }
        else if (aux_buf[j]=='\n'){
            send_msg(fd_client, MSG100);
            free(aux_buf);
            aux_buf=NULL;
            bdestroy(gfl);
            bdestroy(gm);
            bdestroy(gs);
            bdestroy(q);
            close(fd_client);
            fd_list_remove(list, i);
            return;
        }
    }

    fd_list_add_recv_str(list, i, aux_buf, j);

    if (fd_list_get_recv(list, i)){
        buf_server = fd_list_get_recv_str(list, i);
        /*creamos el bstring con todo lo que viene desde el cliente*/
        command = bfromcstr(buf_server);
        
        /*creamos una lista de bstrings separados por espacios*/
        command_list = bsplit(command, ' ');
        /*cargamos en esta variable solo el primer string 
        (ie comando a ejecutar)*/
        command_aux = ((command_list)->entry)[0];
    
        if ((command_list)->qty==1){
            size = blength (command_aux);
            bdelete (command_aux, size-2, 2);
        }
    
        /*el comando pedido fue el get_file_listing*/
        if (bstrcmp(command_aux, gfl) == 0){
            /*Analizo que se haya ingresado el comando sin parametros
            adicionales*/
            if ((command_list->qty) != 1){
                send_msg(fd_client, MSG201);
            }
            else{
                /*en caso de que sea correcto, imprimo en el servidor el
                comando recibido y llamo a la funcion pertinente*/
                printf("Get File Listing\n");
                fd_list_set_cmd(list, i, 0);
                fd_list_set_finish(list, i, false);
            }
        }
        else if (bstrcmp(command_aux, gm) == 0){
            /*el comando pedido fue el get_metadata.
            Analizamos la correcta cantidad de parametros*/
            if ((command_list->qty) != 2){
                send_msg(fd_client, MSG201);
            }
            else{
                printf("Get Metadata\n");
                fd_list_set_cmd(list, i, 1);
                /*en caso de que sea correcto, imprimo en el servidor el
                comando recibido y llamo a la funcion pertinente*/
                size = blength (command_list->entry[1]);
                bdelete (command_list->entry[1], size-2, 2);
                aux = bdata(command_list->entry[1]);
                fd_list_set_filename(list, i, aux);
                fd_list_set_finish(list, i, false);
                /*A continuacion elimino el \r\n del final del nombre
                del archivo*/
            }
        }
        else if (bstrcmp(command_aux, gs) == 0){
            /*el comando pedido fue el get_slice.
            Analizamos la correcta cantidad de parametros*/
            if ((command_list->qty) != 4){
                send_msg(fd_client, MSG201);
            }
            else {
                /*Obtenemos el nombre del archivo y convertimos a enteros
                los parametros adicionales (offset y amount)*/
                aux = bdata(command_list->entry[1]);
                fd_list_set_filename(list, i, aux);
                aux = bdata(command_list->entry[2]);
                offset = strtol(aux, &end_ptr, 10);
                if (end_ptr == aux){
                    send_msg(fd_client, MSG201);
                }
                else{
                    offset = (int)offset;
                    fd_list_set_offset(list, i, offset);
                    aux = bdata(command_list->entry[3]);
                    amount = strtol(aux, &end_ptr, 10);
                    if (end_ptr == aux){
                        send_msg(fd_client, MSG201);
                    }
                    else{
                        amount = (int)amount;
                        fd_list_set_amount(list, i, amount);
                        fd_list_set_transferred(list, i, 0);
                        fd_list_set_finish(list, i, false);
                        printf("Get slice\n");
                        fd_list_set_cmd(list, i, 2);
                    }
                }
            }
        }
        else if (bstrcmp(command_aux, q) == 0){
            /*el comando pedido fue el quit*/
            if ((command_list->qty) != 1){
                send_msg(fd_client, MSG201);
            }
            else{
                printf("Quit\n");
                fd_list_set_cmd(list, i, 3);
            }
        }
        else{
            /*en este caso habria que decir que el comando no se encuentra
            y evaluar la buena formacion del pedido dentro de los
            comandos anteriores*/
            printf("Comando no reconocido\n");
            send_msg(fd_client, MSG200);
        }
        free(buf_server);
        buf_server = NULL;
        bdestroy(command);
        bstrListDestroy(command_list);

        fd_list_reset_recv_str(list, i);
        fd_list_set_recv(list, i, false);
    }
    /*Destruimos las estructuras usadas*/

    free(aux_buf);
    aux_buf=NULL;
    

    bdestroy(gfl);
    bdestroy(gm);
    bdestroy(gs);
    bdestroy(q);
    return;
}
